# -*- coding: utf-8 -*-
import socket
import numpy as np
import struct
import time
import pandas as pd
import matplotlib.pyplot as plt
import math
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense, Activation
from keras.callbacks import EarlyStopping
from keras.models import load_model
from keras.optimizers import SGD
#from hpelm import ELM
#from hpelm import HPELM
#from hpelm import modules

scaler = MinMaxScaler(feature_range=(0, 1))

recv_count = 0
lstm_model = load_model('my_model.h5')
dataset = [0, 1, 2]  # save data for training
dataset.clear()
ann_model = 1  #:0 lstm 1:ann 2:elm
bRetrain = 0  #
print("*********1 Load keras begin  **********************")
from keras.models import load_model

print("*********  Load keras success**********************")

print("*********2 Load keras module  **********************")
new_model = load_model('my_model.h5')
print("*********  Load keras success  ********************")

if (0 == ann_model):
    print("Machine Learning Algorithm: LSTM")
elif (1 == ann_model):
    print("Machine Learning Algorithm: ANN")
elif (2 == ann_model):
    print("Machine Learning Algorithm: ELM")
else:
    print("Machine Learning Algorithm: None")


def predict(x):
    xx = np.array([x])
    x_test = np.reshape(xx, (1, xx.shape[1], 1))

    y_predict = new_model.predict(x_test)
    res = y_predict.tolist()
    # print(x_test, y_predict)
    return res[0][0]


print("*********3 Init server begin  **********************")
serAddress = ('127.0.0.1', 60001)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(serAddress)
print("*********  Init server ok    **********************")


# ANN Model
def ANNModel(x_train, y_train, x_test, y_test):
    global bRetrain
    x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1]))
    x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1]))
    # build model
    model = Sequential()
    # model.add(Dense(3, activation='relu', input_dim= sequence_length))
    # model.add(Dropout(0.5))
    model.add(Dense(3, activation='relu', input_dim=x_train.shape[1]))
    # model.add(Dropout(0.5))
    model.add(Dense(2, activation='relu'))
    # model.add(Dropout(0.5))
    model.add(Dense(1, activation='linear'))
    # model.add(Dropout(0.5))
    # model.add(Dense(categorical_num, activation='softmax'))

    # training
    sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
    model.compile(loss='mse', optimizer=sgd, metrics=['accuracy'])
    early_stop = EarlyStopping(monitor='val_loss', patience=2)
    model.fit(x_train, y_train,
              epochs=30,
              batch_size=100,
              # validation_split = 0.1,
              validation_data=(x_test, y_test),
              callbacks=[early_stop])

    predict = model.predict(x_test)
    print(predict)
    bRetrain = 1
    return model

'''
# ELM Model
def ELMModel(x_train, y_train, x_test, y_test):
    global bRetrain
    x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1]))
    x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1]))
    model = ELM(x_train.shape[1], 1)
    model.add_neurons(20, "rbf_l2")
    model.add_neurons(14, "lin")
    model.train(x_train, y_train)
    predict_train = model.predict(x_train)
    predict_test = model.predict(x_test)
    # err_train = model.error(y_train,predict_train)
    # err_test = model.error(predict_test,y_test)

    bRetrain = 1
    return model
'''

# RNN Model
def RNNModel(x_train, y_train, x_test, y_test):
    global bRetrain
    x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))
    x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))

    model = Sequential()
    model.add(LSTM(input_shape=(None, 1), output_dim=50, return_sequences=True))
    model.add(LSTM(100, return_sequences=False))
    model.add(Dense(output_dim=1))
    model.add(Activation('linear'))

    model.compile(loss='mse', optimizer='rmsprop')
    early_stop = EarlyStopping(monitor='val_loss', patience=5)
    model.fit(x_train, y_train,
              batch_size=100,
              epochs=30,
              # validation_split = 0.2,
              validation_data=(x_test, y_test),
              callbacks=[early_stop])
    # model.save('my_model.h5')
    predict = model.predict(x_test)
    print(predict)
    bRetrain = 1
    return model


# build LSMT model
def build_model():
    model = Sequential()
    model.add(LSTM(input_shape=(None, 1), output_dim=50, return_sequences=True))
    model.add(LSTM(100, return_sequences=False))
    model.add(Dense(output_dim=1))
    model.add(Activation('linear'))

    model.compile(loss='mse', optimizer='rmsprop')
    return model


# train the model
def train_model(x_train, train_y, x_test, test_y):
    x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))
    x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))
    model = build_model()
    try:
        early_stop = EarlyStopping(monitor='val_loss', patience=5)
        model.fit(x_train, train_y, batch_size=50, epochs=100,
                  validation_split=0.2, callbacks=[early_stop])
        # model.save('my_model.h5')
        predict = model.predict(x_test)
        print(model.input.name)
        print(model.output.name)
        predict = np.reshape(predict, (predict.size,))
    except KeyboardInterrupt:
        print(predict)
        print(test_y)
    return model


# gen dataset
def gen_datases(datasets, sequence_length=10, split=0.8):
    datasets = np.array(datasets).astype(float)
    # datasets = datasets.reshape(len(datasets),1)
    data = []
    for i in range(len(datasets) - sequence_length - 1):
        data.append(datasets[i: i + sequence_length + 1])
    reshaped_data = np.array(data).astype('float64')
    # np.random.shuffle(reshaped_data)
    #
    x = reshaped_data[:, :-1]
    y = reshaped_data[:, -1]
    split_boundary = int(reshaped_data.shape[0] * split)
    train_x = x[: split_boundary]
    test_x = x[split_boundary:]
    # train_x = x
    # train_y = y
    # test_x = x
    # test_y = y

    train_y = y[: split_boundary]
    test_y = y[split_boundary:]
    return train_x, train_y, test_x, test_y


def trainning(datasets):
    global lstm_model
    global ann_model

    train_x, train_y, test_x, test_y = gen_datases(datasets)  # gen data
    if (1 == ann_model):
        lstm_model = ANNModel(train_x, train_y, test_x, test_y)  # train
    elif (0 == ann_model):
        lstm_model = RNNModel(train_x, train_y, test_x, test_y)  # train
    elif (2 == ann_model):
        lstm_model = ELMModel(train_x, train_y, test_x, test_y)  # train
    else:
        print("para errer")
    # do nothing
    return lstm_model


def predicting(model, x_predict):
    global bRetrain
    global ann_model
    x_predict = np.array([x_predict])

    # if(bRetrain & ann_model):
    if (bRetrain and (1 == ann_model)):
        x_test = np.reshape(x_predict, (1, x_predict.shape[1]))
    elif (bRetrain and (2 == ann_model)):
        x_test = np.reshape(x_predict, (1, x_predict.shape[1]))
    else:
        x_test = np.reshape(x_predict, (1, x_predict.shape[1], 1))
    y_predict = model.predict(x_test)
    res = y_predict.tolist()
    return res[0][0]


def addDataset(msgid, msg):
    global dataset
    global lstm_model

    # print(msgid)
    if (1024 != msgid):
        return
    if (len(dataset)):
        dataset.append(msg[-1])
    else:
        msg = list(msg)
        dataset = msg
    if (len(dataset) >= 8 * 1000):
        print("training the model ...... wait ........\n")
        lstm_model = trainning(dataset)
        dataset.clear()


print("*********  \n * \n *\n        server is running now!!!!!!!!!!!!!!!!!!! \n**********************")
while True:
    data, cliAddr = s.recvfrom(2048)
    if not data:
        print("client has exist")
        break
    # print(data)
    buf = struct.unpack("IIIII10I", data)
    msg = buf[5:15]
    addDataset(buf[1], msg)
    # print(buf)
    res = predicting(lstm_model, list(msg))
    # res = predict(msg)
    ret = int(res + 0.5)
    if (res > 20):
        print("predict value = %d \r\n", res)
        res = 20

    buffer = struct.pack("IIIIII", 2, buf[1], buf[2], buf[3], buf[4], ret)
    recv_count += 1
    if (0 == (recv_count % 1000)):
        print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "  num=" + str(recv_count))
    s.sendto(buffer, cliAddr)

s.close()
